TypeScript 库开发教程

1. TypeScript 简介和优势

TypeScript 是 JavaScript 的超集,它添加了静态类型和其他现代编程语言的特性。TypeScript 的主要优势包括:

  • 静态类型检查:在编译时捕获错误,减少运行时错误。
  • 更好的代码可读性和维护性:类型注解使得代码更易于理解和维护。
  • 强大的工具支持:IDE 可以提供更好的自动补全、重构和导航功能。
  • 现代语言特性:支持 ES6+ 特性,如类、模块、箭头函数等。

2. 开发环境搭建

2.1 安装 Node.js

首先,确保你已经安装了 Node.js。你可以从 Node.js 官网 下载并安装。

2.2 安装 TypeScript 编译器

使用 npm 安装 TypeScript 编译器:

npm install -g typescript

2.3 创建项目目录

创建一个新的项目目录并初始化 npm:

mkdir my-typescript-library
cd my-typescript-library
npm init -y

2.4 初始化 TypeScript 项目

在项目根目录下运行以下命令来初始化 TypeScript 项目:

tsc --init

这将生成一个 tsconfig.json 文件,用于配置 TypeScript 编译器。

3. 第一个 TypeScript 程序

创建一个 src 目录,并在其中创建一个 index.ts 文件:

// src/index.ts
function greet(name: string): string {
    return `Hello, ${name}!`;
}

console.log(greet("TypeScript"));

编译并运行这个程序:

tsc
node dist/index.js

你应该会看到输出:

Hello, TypeScript!

4. 基本类型和变量声明

TypeScript 支持多种基本类型:

let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
let list: number[] = [1, 2, 3];
let tuple: [string, number] = ["hello", 10];

5. TypeScript 配置文件 (tsconfig.json)

tsconfig.json 文件用于配置 TypeScript 编译器的行为。以下是一个简单的配置示例:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "CommonJS",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

6. 基本类型详解

6.1 Number

TypeScript 中的 number 类型用于表示数字,包括整数和浮点数:

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

6.2 String

string 类型用于表示文本数据:

let color: string = "blue";
color = 'red';

6.3 Boolean

boolean 类型用于表示真或假:

let isDone: boolean = false;

6.4 Array

数组类型可以定义为特定类型的数组:

let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];

6.5 Tuple

元组类型允许表示一个已知元素数量和类型的数组:

let x: [string, number];
x = ["hello", 10];

7. 枚举类型

枚举类型用于定义一组命名常量:

enum Color {
    Red,
    Green,
    Blue
}
let c: Color = Color.Green;

8. Any 和 Unknown 类型

any 类型允许变量存储任何类型的值:

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;

unknown 类型类似于 any,但在类型检查时更安全:

let notSure: unknown = 4;
notSure = "maybe a string instead";
notSure = false;

9. Void, Null 和 Undefined

void 类型表示没有任何类型:

function warnUser(): void {
    console.log("This is my warning message");
}

null 和 undefined 是所有类型的子类型:

let u: undefined = undefined;
let n: null = null;

10. Never 类型

never 类型表示永不存在的值的类型:

function error(message: string): never {
    throw new Error(message);
}

11. 类型断言

类型断言允许你告诉编译器某个值的具体类型:

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

12. 函数声明和表达式

函数声明:

function add(x: number, y: number): number {
    return x + y;
}

函数表达式:

let myAdd = function(x: number, y: number): number {
    return x + y;
};

13. 可选参数和默认参数

可选参数:

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + " " + lastName;
    } else {
        return firstName;
    }
}

默认参数:

function buildName(firstName: string, lastName = "Smith") {
    return firstName + " " + lastName;
}

14. 剩余参数

剩余参数允许函数接受不定数量的参数:

function buildName(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}

15. 函数重载

函数重载允许定义多个函数签名:

function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
    return a + b;
}

16. This 和箭头函数

箭头函数不会改变 this 的上下文:

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

17. 接口定义和使用

接口用于定义对象的形状:

interface LabeledValue {
    label: string;
}

function printLabel(labeledObj: LabeledValue) {
    console.log(labeledObj.label);
}

18. 可选属性和只读属性

可选属性:

interface SquareConfig {
    color?: string;
    width?: number;
}

只读属性:

interface Point {
    readonly x: number;
    readonly y: number;
}

19. 函数类型接口

接口可以描述函数类型:

interface SearchFunc {
    (source: string, subString: string): boolean;
}

20. 类类型接口

接口可以描述类的结构:

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

class Clock implements ClockInterface {
    currentTime: Date = new Date();
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) {}
}

21. 继承接口

接口可以继承其他接口:

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

22. 类型别名 vs 接口

类型别名可以用于定义复杂类型:

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;

23. 类的定义和实例化

定义类:

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

实例化类:

let greeter = new Greeter("world");

24. 继承和多态

继承:

class Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}

class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}

多态:

let dog = new Dog();
dog.bark();
dog.move(10);

25. 访问修饰符 (public, private, protected)

public 是默认的访问修饰符:

class Animal {
    public name: string;
    public constructor(theName: string) { this.name = theName; }
    public move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

private 修饰符限制访问:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

protected 修饰符允许子类访问:

class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}

class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

26. 静态属性和方法

静态属性和方法属于类本身:

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        let xDist = (point.x - Grid.origin.x);
        let yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

27. 抽象类

抽象类不能直接实例化,只能被继承:

abstract class Department {
    constructor(public name: string) {}

    printName(): void {
        console.log("Department name: " + this.name);
    }

    abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Department {
    constructor() {
        super("Accounting and Auditing"); // 在派生类的构造函数中必须调用 super()
    }

    printMeeting(): void {
        console.log("The Accounting Department meets each Monday at 10am.");
    }

    generateReports(): void {
        console.log("Generating accounting reports...");
    }
}

28. 交叉类型

交叉类型将多个类型合并为一个类型:

function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id];
        }
    }
    return result;
}

29. 联合类型

联合类型允许变量具有多种类型之一:

function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

30. 类型保护和类型区分

类型保护用于在运行时检查变量的类型:

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}

31. 可为 null 的类型

TypeScript 支持可为 null 的类型:

let s = "foo";
s = null; // 错误, 'null' 不能赋值给 'string'
let sn: string | null = "bar";
sn = null; // 正确

32. 字面量类型

字面量类型允许你指定变量的具体值:

let x: "hello" = "hello";
x = "hello"; // 正确
x = "howdy"; // 错误

33. 索引类型和映射类型

索引类型允许你查询对象的属性类型:

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
    return names.map(n => o[n]);
}

映射类型允许你创建新的类型:

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}

34. 泛型函数

泛型函数允许你编写可以处理多种类型的函数:

function identity<T>(arg: T): T {
    return arg;
}

35. 泛型接口

泛型接口允许你定义可以处理多种类型的接口:

interface GenericIdentityFn<T> {
    (arg: T): T;
}

36. 泛型类

泛型类允许你定义可以处理多种类型的类:

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

37. 泛型约束

泛型约束允许你限制泛型参数的类型:

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // 现在我们知道它有 .length 属性,所以不会报错
    return arg;
}

38. ES6 模块系统

ES6 模块系统允许你将代码组织为模块:

// math.ts
export function add(x: number, y: number): number {
    return x + y;
}

// app.ts
import { add } from './math';
console.log(add(1, 2)); // 输出 3

39. 命名空间

命名空间用于组织代码:

namespace Validation {
    export interface StringValidator